﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using com.paypal.sdk.util;
using ECSE6770.Models;
using ECSE6770.Payment;

namespace ECSE6770
{
    public partial class UserInterop
    {
        public bool IsRegisteredFor(int moduleId)
        {
            return RegisteredModules().Any(m => m.Id == moduleId);
        }

        /// <summary>
        /// Modules that a user is registered for.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Module> RegisteredModules()
        {
            return this.PurchasedProducts.OfType<Module>().Where(m => !m.Deleted).OrderBy(m => m.Start);
        }

        public IEnumerable<Video> Videos()
        {
            return this.PurchasedProducts.OfType<Video>().Where(v => !v.Deleted).OrderBy(v => v.Title);
        }

        public bool OwnsVideo(Video v)
        {
            return OwnsVideo(v.Id);
        }

        public bool OwnsVideo(int id)
        {
            return Videos().Any(v => v.Id == id);
        }

        public IEnumerable<Module> UpcomingModules()
        {
            DateTime n = DateTime.Now;
            return RegisteredModules().Where(m => !m.IsPast).OrderBy(m => m.Start);
        }

        public IEnumerable<Module> PastModules()
        {
            DateTime n = DateTime.Now;
            return RegisteredModules().Where(m => m.IsPast).OrderByDescending(m => m.End);
        }

        /// <summary>
        /// Modules that this user is speaking at in the future. Only applies for a user that is in the trainer role.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Module> UpcomingModulesTaught()
        {
            return this.ModulesTaught.Where(m => !m.IsPast && !m.Deleted).OrderBy(m => m.Start);
        }

        /// <summary>
        /// Past modules that this user spoke at. Only applies for a user that is in the trainer role.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Module> PastModulesTaught()
        {
            return this.ModulesTaught.Where(m => m.IsPast && !m.Deleted).OrderByDescending(m => m.End);
        }
    }

    [MetadataType(typeof(VenueMetaData))]
    public partial class Venue
    {
        public string ToAddressHtml()
        {
            return String.Format("{0}<br />{1}<br />{2}, {3} {4}", this.Name, this.Address.Replace(Environment.NewLine, "<br />"), this.City, this.State, this.ZipCode);
        }

        public string ToMapQuery()
        {
            return System.Web.HttpUtility.UrlPathEncode(String.Format("{0}, {1}, {2} {3}", this.Address, this.City, this.State, this.ZipCode));
        }
    }

    public class VenueMetaData
    {
        [Required]
        public string Name { get; set; }

        [Required]
        public string Address { get; set; }

        [Required]
        public string City { get; set; }

        [Required]
        public string State { get; set; }

        [Required]
        [DisplayName("Zip Code")]
        public string ZipCode { get; set; }

        [Required]
        public string Country { get; set; }

        [DisplayName("Local Accomodations")]
        [AllowHtml]
        public string LocalAccommodations { get; set; }
    }

    public abstract partial class Product
    {
        public abstract string Category { get; }
        
        public abstract MvcHtmlString LinkToItem(HtmlHelper helper, string linkText);

        public List<Product> ActiveRelatedProducts()
        {
            return RelatedProducts.Where(p => p.Active && !p.Deleted).ToList().Where(p => !p.IsPast).ToList();
        }

        public void UpdateRelatedItems(EntitiesContainer container)
        {
            this.RelatedProducts.Clear();
            if (RelatedInput != null && RelatedInput.Length > 0)
            {
                var ids = RelatedInput.Select(s => Int32.Parse(s)).ToArray();
                var related = from p in container.Products
                              where ids.Contains(p.Id)
                              select p;

                foreach (var p in related)
                    this.RelatedProducts.Add(p);
            }
        }

        public string[] RelatedInput { get; set; }

        public string PriceString()
        {
            return String.Format("{0:c}", this.Price);
        }

        public decimal TaxAmout()
        {
            return Decimal.Round(this.Price * PaymentSettings.TaxRate, 2, MidpointRounding.AwayFromZero);
        }

        public void AddToEncoder(NVPCodec encoder, int itemIndex)
        {
            AddToEncoder(encoder, 0, itemIndex);
        }

        public void AddToEncoder(NVPCodec encoder, int paymentIndex, int itemIndex)
        {
            encoder[String.Format("L_PAYMENTREQUEST_{0}_ITEMCATEGORY{1}", paymentIndex, itemIndex)] = this.Category;
            encoder[String.Format("L_PAYMENTREQUEST_{0}_NAME{1}", paymentIndex, itemIndex)] = this.Title;
            encoder[String.Format("L_PAYMENTREQUEST_{0}_NUMBER{1}", paymentIndex, itemIndex)] = this.Id.ToString();
            encoder[String.Format("L_PAYMENTREQUEST_{0}_QTY{1}", paymentIndex, itemIndex)] = "1"; //Items can only have 1 quantity, for now
            encoder[String.Format("L_PAYMENTREQUEST_{0}_TAXAMT{1}", paymentIndex, itemIndex)] = this.TaxAmout().ToString();
            encoder[String.Format("L_PAYMENTREQUEST_{0}_AMT{1}", paymentIndex, itemIndex)] = this.Price.ToString();
            encoder[String.Format("L_PAYMENTREQUEST_{0}_DESC{1}", paymentIndex, itemIndex)] = this.Title;
        }

        public override bool Equals(object obj)
        {
            Product other = obj as Product;
            return other != null ? other.Id == this.Id : false;
        }

        public override int GetHashCode()
        {
            return this.Id;
        }

        public virtual bool IsPast
        {
            get { return false; }
        }
    }

    [MetadataType(typeof(ModuleMetaData))]
    public partial class Module
    {
        public static string DateFormat
        {
            get { return "MM/dd/yyyy h:mm tt"; }
        }

        public static string FullDateFormat
        {
            get { return "dddd, dd MMMM yyyy h:mm tt"; }
        }

        public override bool IsPast
        {
            get { return DateTime.Now > Start; }
        }

        /* extra props to make this easier to work with when posting a form */
        [Required]
        [DisplayName("Level")]
        public int LevelId { get; set; }

        [Required]
        [DisplayName("Venue")]
        public int VenueId { get; set; }

        [Required]
        [DisplayName("Trainers")]
        public List<string> SpeakerUserNames { get; set; }

        public void BindReferences(EntitiesContainer container)
        {
            this.Level = container.Levels.First(l => l.Id == this.LevelId);
            this.Venue = container.Venues.First(v => v.Id == this.VenueId);
            this.Speakers.Clear();
            foreach (string s in SpeakerUserNames)
                this.Speakers.Add(ContextManager.LoadUserInterop(s));

            UpdateRelatedItems(container);
        }

        public void CheckModifiedReferences(EntitiesContainer container)
        {
            var entry = container.Entry<Module>(this);
            //level
            entry.Reference(m => m.Level).Load();
            if (this.Level.Id != this.LevelId)
                this.Level = container.Levels.First(l => l.Id == this.LevelId);
            //venue
            entry.Reference(m => m.Venue).Load();
            if (this.Venue.Id != this.VenueId)
                this.Venue = container.Venues.First(v => v.Id == this.VenueId);

            entry.Collection(m => m.Speakers).Load();
            this.Speakers.Clear();
            foreach (string s in SpeakerUserNames)
                this.Speakers.Add(ContextManager.LoadUserInterop(s));

            entry.Collection(m => m.RelatedProducts).Load();
            UpdateRelatedItems(container);
        }

        public string WhenHtml()
        {
            return (Start.Date == End.Date) ? 
                String.Concat(Start.ToString("dddd, dd MMMM yyyy"), "<br/ >", Start.ToShortTimeString(), " to ", End.ToShortTimeString()) : 
                String.Concat(Start.ToString(FullDateFormat), " to ", End.ToString(FullDateFormat));
        }

        public IEnumerable<UserProfile> RegisteredUsers()
        {
            return this.Owners.ToList().Select(u => new UserProfile(u));
        }

        public override MvcHtmlString LinkToItem(HtmlHelper helper, string linkText)
        {
            return helper.ActionLink(linkText, "ModuleDetails", "Home", new { id = this.Id }, new { @class = "moduleLink" });
        }

        public override string Category
        {
            get { return "Training Modules"; }
        }
    }

    public class ModuleMetaData
    {
        [Required]
        public string Title { get; set; }

        [AllowHtml]
        public string Teaser { get; set; }

        [AllowHtml]
        public string Description { get; set; }

        [Required]
        public decimal Price { get; set; }

        [Required]
        [DisplayName("Start Date")]
        public DateTime Start { get; set; }

        [Required]
        [DisplayName("End Date")]
        public DateTime End { get; set; }

        [Required]
        public bool Active { get; set; }

        [Required]
        public bool Featured { get; set; }

        [Required]
        [DisplayName("Enrollment Cap")]
        public int MaxEnrollment { get; set; }

        [Required]
        [DisplayName("Contact Email")]
        [DataType(DataType.EmailAddress)]
        public string ContactEmail { get; set; }
    }

    public partial class Level
    {
        public override bool Equals(object obj)
        {
            Level other = obj as Level;
            return other != null ? other.Id == this.Id : false;
        }

        public override int GetHashCode()
        {
            return this.Id;
        }
    }

    [MetadataType(typeof(VideoMetaData))]
    public partial class Video
    {
        public int ScaledWidth
        {
            get
            {
                return Width > VideoManager.ContainerMaxWidth ? VideoManager.ContainerMaxWidth : Width;
            }
        }

        public int ScaledHeight
        {
            get
            {
                if (Width > VideoManager.ContainerMaxWidth)
                    return Decimal.ToInt32((VideoManager.ContainerMaxWidth / (decimal)Width) * Height);
                else
                    return Height;
            }
        }

        public override string Category
        {
            get { return "Videos"; }
        }

        public override MvcHtmlString LinkToItem(HtmlHelper helper, string linkText)
        {
            return helper.ActionLink(linkText, "ViewVideo", "Home", new { id = this.Id }, new { @class = "videoLink" });
        }
    }

    public class VideoMetaData
    {
        [Required]
        public string Title { get; set; }

        [AllowHtml]
        public string Teaser { get; set; }

        [AllowHtml]
        public string Description { get; set; }

        [Required]
        public decimal Price { get; set; }

        [Required]
        public bool Active { get; set; }

        [Required]
        public bool Featured { get; set; }

        [Required]
        public int Width { get; set; }

        [Required]
        public int Height { get; set; }
    }

    [MetadataType(typeof(FeedbackMetaData))]
    public partial class Feedback { }

    public class FeedbackMetaData
    {
        [Required]
        [AllowHtml]
        public string Comments { get; set; }
    }

    public partial class TickerItem
    {
        public decimal? Value { get; set; }
    }
}